commonlibsse_ng\rel\version/
win_api.rs1use crate::rel::version::Version;
10
11pub fn get_file_version(filename: &windows::core::HSTRING) -> Result<Version, FileVersionError> {
29 use core::ptr;
31 use windows::Win32::Storage::FileSystem::{
32 GetFileVersionInfoSizeW, GetFileVersionInfoW, VerQueryValueW,
33 };
34
35 let mut dummy = 0;
36 let size = unsafe { GetFileVersionInfoSizeW(filename, Some(&mut dummy)) };
37 if size == 0 {
38 return Err(FileVersionError::VersionInfoSize { filename: filename.to_string() });
39 }
40
41 let mut buf = vec![0_u8; size as usize];
42
43 if let Err(err) = unsafe { GetFileVersionInfoW(filename, None, size, buf.as_mut_ptr().cast()) }
44 {
45 return Err(FileVersionError::VersionInfoRetrieval { filename: filename.to_string(), err });
46 }
47
48 let ver_str = {
49 let buf_void_ptr = buf.as_mut_ptr().cast();
50 let query_path = windows::core::h!("\\StringFileInfo\\040904B0\\ProductVersion"); let mut ver_buf = ptr::null_mut();
52 let mut ver_len: u32 = 0;
53 if !unsafe { VerQueryValueW(buf_void_ptr, query_path, &mut ver_buf, &mut ver_len) }
54 .as_bool()
55 {
56 return Err(FileVersionError::VersionQuery { filename: filename.to_string() });
57 }
58
59 let slice = unsafe { core::slice::from_raw_parts(ver_buf as *const u16, ver_len as usize) };
60 String::from_utf16_lossy(slice)
61 };
62
63 let mut version = Version::default_const();
64 for (i, token) in ver_str.split('.').take(4).enumerate() {
65 if let Ok(num) = token.parse::<u16>() {
66 version[i] = num;
67 }
68 }
69
70 Ok(version)
71}
72
73#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, snafu::Snafu)]
75pub enum FileVersionError {
76 VersionInfoSize { filename: String },
78
79 VersionInfoRetrieval { filename: String, err: windows::core::Error },
81
82 VersionQuery { filename: String },
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use windows::core::h;
90
91 #[test]
92 fn test_valid_file_version() {
93 let target = h!("C:\\Windows\\splwow64.exe");
95 let version = get_file_version(target).unwrap_or_else(|err| panic!("{err}"));
96 dbg!(version);
97 }
98
99 #[test]
100 fn test_invalid_file_version() {
101 let target = h!("C:\\nonexistent_file.exe");
102 let result = get_file_version(target);
103
104 let expected_err = Err(FileVersionError::VersionInfoSize { filename: target.to_string() });
105
106 assert_eq!(result, expected_err);
107 }
108}